1 /**
2  * Common scan line storage implementations
3  * 
4  * License:
5  *              Copyright Devisualization (Richard Andrew Cattermole) 2014 - 2017.
6  *     Distributed under the Boost Software License, Version 1.0.
7  *        (See accompanying file LICENSE_1_0.txt or copy at
8  *              http://www.boost.org/LICENSE_1_0.txt)
9  */
10 module devisualization.image.storage.base;
11 import std.experimental.color : isColor;
12 import stdx.allocator : IAllocator, ISharedAllocator, theAllocator, processAllocator;
13 
14 /**
15  * A fairly simple image storage type using a horizontal scan line memory order.
16  * 
17  * Will automatically deallocate its memory when it goes out of scope.
18  * Should not be copied or moved around.
19  * 
20  * See_Also:
21  *      ImageStorage
22  */
23 struct ImageStorageHorizontal(Color) if (isColor!Color) {
24     private {
25         size_t width_, height_;
26         IAllocator allocator;
27         Color[][] data;
28     }
29 
30 	@disable
31 	this(this);
32 
33     ///
34     this(size_t width, size_t height, IAllocator allocator = theAllocator()) @trusted {
35         import stdx.allocator : makeArray;
36         this.allocator = allocator;
37 
38         width_ = width;
39         height_ = height;
40         
41         data = allocator.makeArray!(Color[])(width);
42         
43         foreach(_; 0 .. width) {
44             data[_] = allocator.makeArray!Color(height);
45         }
46     }
47 
48 	///
49 	this(size_t width, size_t height, shared(ISharedAllocator) allocator) @trusted shared {
50 		/+import stdx.allocator : makeArray;
51 		this.allocator = allocator;
52 		
53 		width_ = width;
54 		height_ = height;
55 		
56 		data = allocator.makeArray!(Color[])(width);
57 		
58 		foreach(_; 0 .. width) {
59 			data[_] = allocator.makeArray!Color(height);
60 		}+/
61 	}
62 
63 	~this() @trusted {
64 		import stdx.allocator : dispose;
65 
66 		foreach(_; 0 .. width_) {
67 			allocator.dispose(data[_]);
68 		}
69 
70 		allocator.dispose(data);
71 	}
72 
73     @property {
74         ///
75 		size_t width() @nogc nothrow @safe { return width_; }
76 		///
77 		size_t width() @nogc nothrow @safe shared { return width_; }
78 
79         ///
80 		size_t height() @nogc nothrow @safe { return height_; }
81 		///
82 		size_t height() @nogc nothrow @safe shared { return height_; }
83     }
84 
85     ///
86 	Color getPixel(size_t x, size_t y) @nogc @safe { return data[x][y]; }
87 	///
88 	Color getPixel(size_t x, size_t y) @nogc @safe shared { return data[x][y]; }
89 
90     ///
91 	void setPixel(size_t x, size_t y, Color value) @nogc @safe { data[x][y] = value; }
92 	///
93 	void setPixel(size_t x, size_t y, Color value) @nogc @safe shared { data[x][y] = value; }
94 
95     ///
96 	Color opIndex(size_t x, size_t y) @nogc @safe { return getPixel(x, y); }
97 	Color opIndex(size_t x, size_t y) @nogc @safe shared { return getPixel(x, y); }
98 
99     ///
100 	void opIndexAssign(Color value, size_t x, size_t y) @nogc @safe { setPixel(x, y, value); }
101 	void opIndexAssign(Color value, size_t x, size_t y) @nogc @safe shared { setPixel(x, y, value); }
102 
103 	bool resize(size_t newWidth, size_t newHeight) @trusted shared
104 	{ return (cast()this).resize(newWidth, newHeight); }
105 
106     ///
107     bool resize(size_t newWidth, size_t newHeight) @trusted
108     in {
109         assert(newWidth > 0);
110         assert(newHeight > 0);
111     } body { 
112         import stdx.allocator : expandArray, shrinkArray, makeArray;
113 
114         if (newWidth == width_ && newHeight == height_) return true;
115         size_t deltaHeight;
116 
117         if (width_ < newWidth) {
118             assert(allocator.expandArray!(Color[])(data, newWidth - width_));
119         } else if (width_ == newWidth) {
120         } else {
121             assert(allocator.shrinkArray!(Color[])(data, width_ - newWidth));
122         }
123 
124         if (height_ < newHeight)
125             deltaHeight = newHeight - height_;
126         else
127             deltaHeight = height_ - newHeight;
128 
129         foreach(_; 0 .. width_) {
130             if (height_ < newHeight) {
131                 assert(allocator.expandArray!Color(data[_], deltaHeight));
132             } else if (height_ == newHeight) {
133             } else {
134                 assert(allocator.shrinkArray!Color(data[_], deltaHeight));
135             }
136         }
137         
138         if (width_ < newWidth) {
139             foreach(_; width_-1 .. newWidth) {
140                 auto got = allocator.makeArray!Color(newHeight);
141                 assert(got !is null);
142                 data[_] = got;
143             }
144         }
145 
146         width_ = newWidth;
147         height_ = newHeight;
148         return true;
149     }
150 }
151 
152 ///
153 unittest {
154     import std.experimental.color;
155     ImageStorageHorizontal!RGB8 image = ImageStorageHorizontal!RGB8(1, 1);
156     image.resize(2, 2);
157 
158     assert(image.width == 2);
159     assert(image.height == 2);
160     assert(image[1, 1] == image[0, 0]);
161 }
162 
163 /**
164  * A fairly simple image storage type using a vertical scan line memory order.
165  * 
166  * Will automatically deallocate its memory when it goes out of scope.
167  * Should not be copied or moved around.
168  * 
169  * See_Also:
170  *      ImageStorage
171  */
172 struct ImageStorageVertical(Color) if (isColor!Color) {
173     private {
174         size_t width_, height_;
175         IAllocator allocator;
176         Color[][] data;
177     }
178 
179     ///
180     this(size_t width, size_t height, IAllocator allocator = theAllocator()) @trusted {
181         import stdx.allocator : makeArray;
182         this.allocator = allocator;
183 
184         width_ = width;
185         height_ = height;
186         
187         data = allocator.makeArray!(Color[])(height);
188         
189         foreach(_; 0 .. height) {
190             data[_] = allocator.makeArray!Color(width);
191         }
192     }
193 
194 	~this() @trusted {
195 		import stdx.allocator : dispose;
196 
197 		foreach(_; 0 .. width_) {
198 			allocator.dispose(data[_]);
199 		}
200 		
201 		allocator.dispose(data);
202 	}
203 
204     @property {
205         ///
206         size_t width() @nogc nothrow @safe { return width_; }
207 
208         ///
209         size_t height() @nogc nothrow @safe { return height_; }
210     }
211 
212     ///
213     Color getPixel(size_t x, size_t y) @nogc @safe { return data[y][x]; }
214 
215     ///
216     void setPixel(size_t x, size_t y, Color value) @nogc  @safe{ data[y][x] = value; }
217 
218     ///
219     Color opIndex(size_t x, size_t y) @nogc  @safe{ return getPixel(x, y); }
220 
221     ///
222     void opIndexAssign(Color value, size_t x, size_t y) @nogc @safe { setPixel(x, y, value); }
223 
224     ///
225     bool resize(size_t newWidth, size_t newHeight) @trusted
226     in {
227         assert(newWidth > 0);
228         assert(newHeight > 0);
229     } body { 
230         import stdx.allocator : expandArray, shrinkArray, makeArray;
231 
232         if (newWidth == width_ && newHeight == height_) return true;
233         size_t deltaWidth;
234 
235         if (height_ < newHeight) {
236             assert(allocator.expandArray!(Color[])(data, newHeight - height_));
237         } else if (height_ == newHeight) {
238         } else {
239             assert(allocator.shrinkArray!(Color[])(data, height_ - newHeight));
240         }
241 
242         if (width_ < newWidth)
243             deltaWidth = newWidth - width_;
244         else
245             deltaWidth = width_ - newWidth;
246 
247         foreach(_; 0 .. height_) {
248             if (width_ < newWidth) {
249                 assert(allocator.expandArray!Color(data[_], deltaWidth));
250             } else if (width_ == newWidth) {
251             } else {
252                 assert(allocator.shrinkArray!Color(data[_], deltaWidth));
253             }
254         }
255 
256         if (height_ < newHeight) {
257             foreach(_; height_-1 .. newHeight) {
258                 auto got = allocator.makeArray!Color(newWidth);
259                 assert(got !is null);
260                 data[_] = got;
261             }
262         }
263 
264         width_ = newWidth;
265         height_ = newHeight;
266         return true;
267     }
268 }
269 
270 ///
271 unittest {
272     import std.experimental.color;
273     ImageStorageVertical!RGB8 image = ImageStorageVertical!RGB8(1, 1);
274     image.resize(2, 2);
275 
276     assert(image.width == 2);
277     assert(image.height == 2);
278     assert(image[1, 1] == image[0, 0]);
279 }